package com.apobates.forum.utils;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 分页类
 * @author xiaofanku
 * @since 20200517
 */
public final class Pagination {
    // 连接地址
    private final String link;
    // 当前页码值
    private final int currentPage;
    private final PaginationHelper helper;
    // 页码的参数
    private final String pageParameter;
    // 最多显示多少页
    private final int showTotalPages;
    // 连接的分隔符
    private final String delimiter;
    //
    public final static String PAGE_HOME = "Home";
    public final static String PAGE_END = "Tail";
    public final static String PAGE_TOTAL = "Size";
    public final static String PAGE_PREV = "Prev";
    public final static String PAGE_NEXT = "Next";
    
    //
    /**
     * 初始化,分页的查询参数(p),不限制显示的页数
     *
     * @param link 连接地址
     * @param page 当前页码
     * @param pageSize 每页显示的记数数
     * @param total 总记录数
     */
    public Pagination(String link, int page, int pageSize, long total) {
        this(link, page, pageSize, total, "p", -1);
    }
    
    /**
     * 初始化,不限制显示的页数
     *
     * @param link 连接地址
     * @param page 当前页码
     * @param pageSize 每页显示的记数数
     * @param total 总记录数
     * @param pageParameter 分页的查询参数
     */
    public Pagination(String link, int page, int pageSize, long total, String pageParameter) {
        this(link, page, pageSize, total, pageParameter, -1);
    }
    
    /**
     * 初始化
     *
     * @param link 连接地址
     * @param page 当前页码
     * @param pageSize 每页显示的记数数
     * @param total 总记录数
     * @param pageParameter 分页的查询参数
     * @param showMaxPages 最多显示多少页
     */
    public Pagination(String link, int page, int pageSize, long total, String pageParameter, int showMaxPages) {
        this.link = link;
        this.currentPage = page;
        this.helper = new PaginationHelper(pageSize, total);
        //
        this.pageParameter = pageParameter;
        this.showTotalPages = showMaxPages;
        this.delimiter = link.contains("?") ? "&" : "?";
    }
    
    // 首页
    private String getHomeLink() {
        return link + delimiter + pageParameter + "=1";
    }
    
    // 尾页
    private String getTailLink() {
        return link + delimiter + pageParameter + "=" + showMaxPageNumber();
    }
    
    /**
     * 显示的最大页码值/共几页
     *
     * @return
     */
    public int showMaxPageNumber() {
        if (this.showTotalPages == -1) { // 不限制
            return helper.getTotalPage();
        } else {
            return (helper.getTotalPage() > showTotalPages) ? showTotalPages : helper.getTotalPage();
        }
    }
    
    // 有上一页吗
    private boolean hasPrevLink() {
        return currentPage > 1;
    }
    
    // 有下一页吗
    private boolean hasNextLink() {
        return currentPage < showMaxPageNumber();
    }
    
    /**
     * 上一页的连接
     *
     * @param isShowEmptyLink 是否显示无效的连接,false(null),true(#)
     * @return
     */
    private String getPrevLink(boolean isShowEmptyLink) {
        if (hasPrevLink()) {
            return link + delimiter + pageParameter + "=" + (currentPage - 1);
        }
        return (isShowEmptyLink) ? "#" : null;
    }
    
    /**
     * 下一页的连接
     *
     * @param isShowEmptyLink 是否显示无效的连接,false(null),true(#)
     * @return
     */
    private String getNextLink(boolean isShowEmptyLink) {
        if (hasNextLink()) {
            return link + delimiter + pageParameter + "=" + (currentPage + 1);
        }
        return (isShowEmptyLink) ? "#" : null;
    }
    
    /**
     * 当前页显示的记录的最少ID(总记录数的步位) ID不表示实体的@ID
     *
     * @return
     */
    public int getViewMinRecordId() {
        return (currentPage - 1) * helper.getPageSize() + 1;
    }
    
    /**
     * 当前页显示的记录的最大ID(总记录数的步位)
     *
     * @return
     */
    public long getViewMaxRecordId() {
        int maxId = currentPage * helper.getPageSize();
        return (maxId <= helper.getTotal()) ? maxId : helper.getTotal();
    }
    
    /**
     * 显示:首页(home)|上一页(prev)|下一页(next)|尾页(end)总页数(size)
     *
     * @param isShowEmptyLink 当上一页和下一页不存在时是否显示, false不显示无效的连接,true显示
     * @param notPageNumberTitle key:以PAGE_开头的类变量,value为相应的语言描述
     * @return
     */
    public Map<String, String> draw(final boolean isShowEmptyLink, final Map<String, String> notPageNumberTitle) {
        Map<String, String> struct = new LinkedHashMap<>();
        if (showMaxPageNumber() <= 0) {
            return struct;
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_HOME)) {
            struct.put(notPageNumberTitle.get(Pagination.PAGE_HOME), getHomeLink());
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_PREV)) {
            String pl = getPrevLink(isShowEmptyLink);
            if (null != pl) {
                struct.put(notPageNumberTitle.get(Pagination.PAGE_PREV), pl);
            }
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_NEXT)) {
            String nl = getNextLink(isShowEmptyLink);
            if (null != nl) {
                struct.put(notPageNumberTitle.get(Pagination.PAGE_NEXT), nl);
            }
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_END)) {
            struct.put(notPageNumberTitle.get(Pagination.PAGE_END), getTailLink());
        }
        struct.put(Pagination.PAGE_TOTAL, showMaxPageNumber() + "");
        return struct;
    }
    
    /**
     * 显示:首页(home)|上一页(prev)|下一页(next)|尾页(end)总页数(size)
     *
     * @param isShowEmptyLink 当上一页和下一页不存在时是否显示, false不显示无效的连接,true显示
     * @return
     */
    public Map<String, String> draw(final boolean isShowEmptyLink) {
        Map<String, String> struct = new LinkedHashMap<>();
        if (showMaxPageNumber() <= 0) {
            return struct;
        }
        struct.put(Pagination.PAGE_HOME, getHomeLink());
        String pl = getPrevLink(isShowEmptyLink);
        if (null != pl) {
            struct.put(Pagination.PAGE_PREV, pl);
        }
        String nl = getNextLink(isShowEmptyLink);
        if (null != nl) {
            struct.put(Pagination.PAGE_NEXT, nl);
        }
        //
        struct.put(Pagination.PAGE_END, getTailLink());
        struct.put(Pagination.PAGE_TOTAL, showMaxPageNumber() + "");
        return struct;
    }
    
    /**
     * {首页|上一页|1|2|...|下一页|尾页}
     *
     * @param isShowEmptyLink 当上一页和下一页不存在时是否显示, false不显示无效的连接,true显示
     * @param numberRange 显示的页码范围,若小于5默认为5
     * @param notPageNumberTitle key:以PAGE_开头的类变量,value为相应的语言描述
     * @return
     */
    public Map<String, String> draw(final boolean isShowEmptyLink, final int numberRange, final Map<String, String> notPageNumberTitle) {
        Map<String, String> struct = new LinkedHashMap<>();
        if (showMaxPageNumber() <= 0) {
            return struct;
        }
        //
        if (notPageNumberTitle.containsKey(Pagination.PAGE_HOME)) {
            struct.put(notPageNumberTitle.get(Pagination.PAGE_HOME), getHomeLink());
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_PREV)) {
            String pl = getPrevLink(isShowEmptyLink);
            if (null != pl) {
                struct.put(notPageNumberTitle.get(Pagination.PAGE_PREV), pl);
            }
        }
        // 数字
        Map<Integer, String> pageNumber = drawPagination(numberRange);
        // 增补1... | ... EP(最后一页)
        Set<Integer> pnKeySet = pageNumber.keySet();
        for (Integer pn : pnKeySet) {
            struct.put(pn + "", pageNumber.get(pn));
        }
        //
        if (notPageNumberTitle.containsKey(Pagination.PAGE_NEXT)) {
            String nl = getNextLink(isShowEmptyLink);
            if (null != nl) {
                struct.put(notPageNumberTitle.get(Pagination.PAGE_NEXT), nl);
            }
        }
        if (notPageNumberTitle.containsKey(Pagination.PAGE_END)) {
            struct.put(notPageNumberTitle.get(Pagination.PAGE_END), getTailLink());
        }
        return struct;
    }
    
    /**
     * 打印页码数字
     *
     * @param numberRange 显示的页码范围,若小于5默认为5
     * @return
     */
    public Map<Integer, String> drawPagination(final int numberRange) {
        final int pageCount = showMaxPageNumber(); //显示的页码上限
        final Map<Integer, String> pagination = new LinkedHashMap<>();
        // 防止无数据时显示1
        if (pageCount < 1) {
            return pagination;
        }
        final String procssurl = link + delimiter + pageParameter + "=";
        int[] d = getPagePanel(numberRange);
        for (int i = d[0]; i <= d[1]; i++) {
            pagination.put(i, procssurl + i);
        }
        return pagination;
    }
    
    /**
     * drawPagination方法的助手方法.私有 返回一个只有两个元素的数组. key说明: 
     * index=0:可视页码的下限数字
     * index=1:可视页码的上限数字
     *
     * @param numberRange 显示的页码范围,若小于5默认为5
     */
    private int[] getPagePanel(final int numberRange) {
        int safeNumberRange = (numberRange<5)?5:numberRange;
        int[] data = {1, 1};
        int pageCount = showMaxPageNumber(); //显示的页码上限
        int nR = currentPage / safeNumberRange;
        if(currentPage % safeNumberRange>0){
            nR += 1;
        }
        int rangeEnd = nR * safeNumberRange;
        if(rangeEnd > pageCount){
            rangeEnd = pageCount;
        }
        data[0] = nR * safeNumberRange - safeNumberRange + 1; //下限
        data[1] = rangeEnd; //上限
        return data;
    }
    
    // 内部助手类
    private class PaginationHelper {
        // 每页显示的记录数
        private final int pageSize;
        // 总记录数
        private final long total;
        // 总页数
        private final int totalPage;
        
        public PaginationHelper(int pageSize, long total) {
            super();
            this.pageSize = pageSize;
            this.total = total;
            this.totalPage = (total > 0) ? calcTotalPage(total, pageSize) : 1;
        }
        
        private int calcTotalPage(long total, int pageSize) {
            if (total <= pageSize) {
                return 1;
            }
            // 整除
            long totalPages = total / pageSize;
            // 不整除
            double a = total % pageSize;
            if (a != 0) {
                totalPages += 1;
            }
            return Long.valueOf(totalPages).intValue();
        }
        
        public int getPageSize() {
            return pageSize;
        }
        
        public long getTotal() {
            return total;
        }
        
        public int getTotalPage() {
            return totalPage;
        }
    }
}